package com.example.backend.service.storage.impl;

import com.example.backend.service.storage.MinIOService;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.*;


@RequiredArgsConstructor
@Service
public class MinIOServiceImpl implements MinIOService {

    // 公开访问策略缓存（避免重复设置）
    private static final Set<String> publicBuckets = Collections.synchronizedSet(new HashSet<>());
    @Autowired
    private MinioClient minioClient;
    @Value("${minio.bucket}")
    private String defaultBucket;
    @Value("${minio.presigned-expiry:604800}") // 默认7天(单位秒)
    private int presignedExpiry;

    @Value("${minio.endpoint}")
    private String minioEndpoint;

    @Value("${minio.avatar-directory:avatars/}")
    private String avatarDirectory;

    /**
     * 设置存储桶为公开访问（永久访问）
     */
    private void setBucketPublicAccess(String bucketName) throws Exception {
        if (publicBuckets.contains(bucketName)) {
            return;
        }

        // 设置存储桶策略允许公开访问
        String policyJson = String.format(
                "{" +
                        "  \"Version\": \"2012-10-17\"," +
                        "  \"Statement\": [" +
                        "    {" +
                        "      \"Effect\": \"Allow\"," +
                        "      \"Principal\": \"*\"," +
                        "      \"Action\": [\"s3:GetObject\"]," +
                        "      \"Resource\": [\"arn:aws:s3:::%s/*\"]" +
                        "    }" +
                        "  ]" +
                        "}",
                bucketName
        );

        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                .bucket(bucketName)
                .config(String.valueOf(new ObjectMapper().readTree(policyJson)))
                .build());

        publicBuckets.add(bucketName);
    }

    /**
     * 确保存储桶存在并设置为公开访问
     */
    private void ensurePublicBucketExists(String bucketName) throws Exception {
        if (!bucketExists(bucketName)) {
            createBucket(bucketName);
        }
        setBucketPublicAccess(bucketName);
    }

    /**
     * 上传用户头像（永不过期访问）
     */
    @Override
    public String uploadAvatar(MultipartFile avatarFile, String userId) throws Exception {
        // 格式化头像文件名：avatars/user-12345.webp
        String extension = Objects.requireNonNull(avatarFile.getContentType())
                .split("/")[1];
        String objectName = avatarDirectory + "user-" + userId + "." + extension;

        // 确保存储桶设置为公开
        ensurePublicBucketExists(defaultBucket);

        // 上传文件
        try (InputStream inputStream = avatarFile.getInputStream()) {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(defaultBucket)
                    .object(objectName)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(avatarFile.getContentType())
                    .build());
        }

        // 返回永久访问URL
        return getPermanentUrl(objectName);
    }

    /**
     * 获取头像永久访问URL
     */
    @Override
    public String getAvatarUrl(String userId, String contentType) {
        String extension = contentType.split("/")[1];
        String objectName = avatarDirectory + "user-" + userId + "." + extension;
        return getPermanentUrl(objectName);
    }

    @Override
    public void deleteAvatar(String logoUrl) throws Exception {
        deleteFile(defaultBucket, logoUrl);
    }

    /**
     * 生成永久访问URL
     */
    @Override
    public String getPermanentUrl(String objectName) {
        return String.format("%s/%s/%s",
                minioEndpoint,
                defaultBucket,
                objectName.replace(" ", "%20"));
    }

    /**
     * 生成永久访问URL（指定对象名）
     */
    @Override
    public String getPermanentUrl(String bucketName, String objectName) {
        return String.format("%s/%s/%s",
                minioEndpoint,
                bucketName,
                objectName.replace(" ", "%20"));
    }

    /**
     * 检查存储桶是否存在
     */
    @Override
    public boolean bucketExists(String bucketName) throws Exception {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 创建存储桶
     */
    @Override
    public void createBucket(String bucketName) throws Exception {
        if (!bucketExists(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 确保存储桶存在（不存在则创建）
     */
    private void ensureBucketExists(String bucketName) throws Exception {
        if (!bucketExists(bucketName)) {
            createBucket(bucketName);
        }
    }

    /**
     * 核心上传方法 - 返回预签名URL
     */
    @Override
    public String uploadFile(InputStream inputStream, String bucketName, String objectName, String contentType) throws Exception {
        ensureBucketExists(bucketName);

        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(inputStream, inputStream.available(), -1)
                .contentType(contentType)
                .build());

        return getPresignedUrl(bucketName, objectName);
    }

    /**
     * 上传文件（使用默认存储桶）
     */
    @Override
    public String uploadFile(InputStream inputStream, String objectName, String contentType) throws Exception {
        return uploadFile(inputStream, defaultBucket, objectName, contentType);
    }

    /**
     * 上传MultipartFile（指定存储桶）- 返回预签名URL
     */
    @Override
    public String uploadFile(MultipartFile file, String bucketName, String objectName) throws Exception {
        try (InputStream inputStream = file.getInputStream()) {
            return uploadFile(inputStream, bucketName, objectName, file.getContentType());
        }
    }

    /**
     * 上传MultipartFile（使用默认存储桶）- 返回预签名URL
     */
    @Override
    public String uploadFile(MultipartFile file, String objectName) throws Exception {
        return uploadFile(file, defaultBucket, objectName);
    }

    /**
     * 下载文件
     */
    @Override
    public InputStream downloadFile(String bucketName, String objectName) throws Exception {
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 删除文件
     */
    @Override
    public void deleteFile(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 列出所有存储桶
     */
    @Override
    public List<Bucket> listBuckets() throws Exception {
        return minioClient.listBuckets();
    }

    /**
     * 获取文件URL（带签名，临时访问）
     */
    @Override
    public String getPresignedUrl(String bucketName, String objectName) throws Exception {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectName)
                .expiry(presignedExpiry)
                .build());
    }

    /**
     * 获取带自定义有效期的预签名URL
     */
    @Override
    public String getPresignedUrl(String bucketName, String objectName, int expirySeconds) throws Exception {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(objectName)
                .expiry(expirySeconds)
                .build());
    }
}
